package com.gitee.cnlongdb.server.connect.nettymission;

import java.nio.charset.Charset;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.cnlongdb.basic.config.LongDbConfig;
import com.gitee.cnlongdb.basic.constant.LongDbConstant;
import com.gitee.cnlongdb.basic.enums.sysnc.LongdbDataSyncEnms;
import com.gitee.cnlongdb.server.connect.LongDbConnect;
import com.gitee.cnlongdb.server.connect.nettymission.handler.NettyServerHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 系统名称：LongdbNettyServer.java
 * 模块名称：
 * 模块描述：netty-网络通信服务端
 * 功能列表：
 * 模块作者：ZOUYONG
 * 开发时间：2019年4月10日 下午9:44:23
 * 模块路径：com.gitee.cnlongdb.server.connect.nettymission.LongdbNettyServer
 * 更新记录：
 */
public class LongdbNettyServer extends Thread {

	private final Logger logger = (Logger) LoggerFactory.getLogger(LongdbNettyServer.class);

	// 数据库连接对象
	private LongDbConnect connect;
	// 默认缓冲区大小
	private int buffSize = LongDbConfig.longdbSocketBufferSize;
	// 执行对象
	private Object object;

	// 数据对象构造函数
	public LongdbNettyServer(LongDbConnect connect, Object object) {
		this.connect = connect;
		this.object = object;
	}

	/**
	 * 功能描述：开启netty服务监听
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年4月11日 上午10:54:12
	 × 返回数据：void
	 */
	public void run() 
	{
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			ServerBootstrap sb = new ServerBootstrap();
			sb.option(ChannelOption.SO_BACKLOG, buffSize); // 允许连接数
			sb.group(group, bossGroup) // 绑定线程池
				.channel(NioServerSocketChannel.class) // 指定使用的channel
				.localAddress(this.connect.getDataBasePort()) // 绑定监听端口
				.childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						/**
						 * 解决掉包和粘包问题
						 * 这里设置的是（LongdbDataSyncEnms.cnlongNettySplitUnpooled.name()）为包与包之间的分隔符
						 * 所以每出现一次（LongdbDataSyncEnms.cnlongNettySplitUnpooled.name()）就切割为一个包，
						 * 注意的是设置为分隔符的（LongdbDataSyncEnms.cnlongNettySplitUnpooled.name()）将不会出现在最后的信息里。
						 * 并且这里设置了最大长度为(LongDbConstant.cnlongNettyDataPackageLength)个字节，
						 * 如果超过了(LongDbConstant.cnlongNettyDataPackageLength)个字节还没有出现分隔符，会抛出异常
						 * -------------------------------------------------------------------------------------------
						 * 客户端IP: ch.localAddress().getHostName() ";端口:" + ch.localAddress().getPort()
						 */
						ByteBuf buf = Unpooled.copiedBuffer(LongdbDataSyncEnms.cnlongNettySplitUnpooled.name(), Charset.forName("utf-8"));
						DelimiterBasedFrameDecoder decoder = new DelimiterBasedFrameDecoder(LongDbConstant.cnlongNettyDataPackageLength, buf);
						ch.pipeline().addLast(decoder);
						
						ch.pipeline().addLast(new StringEncoder(Charset.forName("utf-8")));
						ch.pipeline().addLast(new NettyServerHandler(object)); // 客户端触发操作
						ch.pipeline().addLast(new ByteArrayEncoder());
						ch.pipeline().addLast(new ChunkedWriteHandler());
					}
				});
			ChannelFuture cf = sb.bind().sync(); // 服务器异步创建绑定
			logger.debug("CnLongdb数据库服务已启动，服务正在监听：{}", cf.channel().localAddress());
			cf.channel().closeFuture().sync(); // 关闭服务器通道
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
		finally {
			try {
				if (null != group) {
					group.shutdownGracefully().sync(); // 释放线程池资源
					bossGroup.shutdownGracefully().sync();
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}

}
